package com.tubeonfire.search;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
import java.util.logging.Logger;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;

import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.ListRequest;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;
import com.google.appengine.api.search.SortExpression;
import com.google.appengine.api.search.SortOptions;
import com.tubeonfire.entity.SpecialTube;
import com.tubeonfire.entity.Tube;
import com.tubeonfire.model.SpecialTubeModel;
import com.tubeonfire.model.admin.SiteConfigModel;
import com.tubeonfire.search.admin.TubeSearchEngine;

public class TubeSearchModel {

	private static Index INDEX = SearchServiceFactory.getSearchService()
			.getIndex(IndexSpec.newBuilder().setName("tube"));
	private static final Logger log = Logger.getLogger(TubeSearchModel.class
			.getName());

	private static Cache cache = null;

	private static String cacheSide = "frontEndSearch_";

	private static String cachePrefix = "tubeModel_";

	private static boolean isRegisted = false;

	private static TreeMap<String, String> mapCacheKey = new TreeMap<String, String>();

	private int limit = 30;

	private int page = 1;

	private int totalResult = 0;

	private int totalReturn = 0;

	private int totalPage = 1;

	private List<Tube> listResult = new ArrayList<Tube>();

	public TubeSearchModel() {
		initCache();
	}

	public void setTotalResult(int totalResult) {
		this.totalResult = totalResult;
	}

	public int getTotalPage() {
		totalPage = totalResult / limit;
		if ((totalResult % limit) > 0) {
			totalPage += 1;
		}
		return totalPage;
	}

	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}

	public int getLimit() {
		return limit;
	}

	public void setLimit(int limit) {
		this.limit = limit;
	}

	public boolean isHasNextPage() {
		if (totalResult != 0) {
			return totalResult > (page * limit);
		} else {
			return false;
		}
	}

	public boolean isHasPreviousPage() {
		return page > 1;
	}

	public int getTotalResult() {
		return totalResult;
	}

	public int getTotalReturn() {
		return totalReturn;
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public List<Tube> getListResult() {
		return listResult;
	}

	public void setListResult(List<Tube> listResult) {
		this.listResult = listResult;
	}

	public static String getCacheSide() {
		return cacheSide;
	}

	public static String getCachePrefix() {
		return cachePrefix;
	}

	public static void initCache() {
		if (!isRegisted) {
			isRegisted = true;
			try {
				cache = CacheManager.getInstance().getCacheFactory()
						.createCache(Collections.emptyMap());
			} catch (CacheException e) {
				log.warning(e.toString());
				e.printStackTrace();
				isRegisted = false;
			}
		}
	}

	public void countTotal() {
		ListRequest request = ListRequest.newBuilder()
				.setReturningIdsOnly(true).setLimit(1000).build();
		totalResult = (int) INDEX.listDocuments(request).getResults().size();
	}

	@SuppressWarnings("unchecked")
	public static Tube getById(String id) {
		try {
			initCache();
			String prefix = cachePrefix + "id_" + id;
			Tube obj = new Tube();
			if (cache != null && cache.containsKey(prefix)) {
				obj = (Tube) cache.get(prefix);
			} else {
				QueryOptions options = QueryOptions.newBuilder().setLimit(1)
						.build();
				Query query = Query.newBuilder().setOptions(options)
						.build("id:\"" + id + "\"");
				Results<ScoredDocument> docResult = INDEX.search(query);
				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
					}
				}
				if (obj != null && obj.getId().length() > 0) {
					cache.put(prefix, obj);
				} else {
					obj = null;
				}
			}
			return obj;
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareList() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "list_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "list_" + "TotalResult";
			String limitPrefix = cachePrefix + "list_" + "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;

				options = QueryOptions
						.newBuilder()
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options).build("");

				docResult = INDEX.search(query);
				if (cache != null && cache.containsKey(limitPrefix)
						&& cache.containsKey(totalResultPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					countTotal();
				}
				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						listResult.add(TubeSearchEngine
								.documentToObjectByReflection(scoredDocument));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareRecentView() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "recentView_";
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;

				options = QueryOptions
						.newBuilder()
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options).build("");

				docResult = INDEX.search(query);

				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						listResult.add(TubeSearchEngine
								.documentToObjectByReflection(scoredDocument));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubes() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "tubes_" + page;
			String totalResultPrefix = cachePrefix + "tubes_" + "TotalResult";
			String limitPrefix = cachePrefix + "tubes_" + "Limit";
			mapCacheKey.put(prefix, prefix);
			mapCacheKey.put(totalResultPrefix, totalResultPrefix);
			mapCacheKey.put(limitPrefix, limitPrefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;

				options = QueryOptions
						.newBuilder()
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options).build("");

				docResult = INDEX.search(query);
				if (cache != null && cache.containsKey(limitPrefix)
						&& cache.containsKey(totalResultPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					countTotal();
				}
				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						listResult.add(TubeSearchEngine
								.documentToObjectByReflection(scoredDocument));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubeByPlaylist(String playlistId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "playlist_" + playlistId
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "playlist_" + playlistId
					+ "TotalResult";
			String limitPrefix = cachePrefix + "playlist_" + playlistId
					+ "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("playlistId:" + playlistId);
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					options = QueryOptions.newBuilder()
							.setNumberFoundAccuracy(1000).build();
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);
					totalResult = (int) docResult.getNumberFound();
				}

				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				totalReturn = (int) docResult.getNumberReturned();
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubeByChannel(String channelId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "channelId_" + channelId
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "channelId_" + channelId
					+ "TotalResult";
			String limitPrefix = cachePrefix + "channelId_" + channelId
					+ "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("channelId:" + channelId);
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					options = QueryOptions.newBuilder()
							.setNumberFoundAccuracy(1000).build();
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);
					totalResult = (int) docResult.getNumberFound();
				}

				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				totalReturn = (int) docResult.getNumberReturned();
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubeByAuthor(String authorId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "authorId_" + authorId
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "authorId_" + authorId
					+ "TotalResult";
			String limitPrefix = cachePrefix + "authorId_" + authorId + "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("authorUrl=\"" + authorId + "\"");
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					options = QueryOptions.newBuilder()
							.setNumberFoundAccuracy(1000).build();
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);
					totalResult = (int) docResult.getNumberFound();
				}

				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				System.out.println(queryString.toString());
				docResult = INDEX.search(query);
				totalReturn = (int) docResult.getNumberReturned();
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareByTag(String tag) {
		try {
			String prefix = cacheSide + cachePrefix + "tag_" + tag + "_page_"
					+ page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "tag_" + tag
					+ "TotalResult";
			String limitPrefix = cachePrefix + "tag_" + tag + "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				listResult = new ArrayList<Tube>();
				StringBuilder queryString = new StringBuilder();
				queryString.append("tags:" + tag + " OR title:" + tag);
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					options = QueryOptions.newBuilder()
							.setNumberFoundAccuracy(1000).build();
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);
					totalResult = (int) docResult.getNumberFound();
				}
				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				totalReturn = (int) docResult.getNumberReturned();
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareBanner() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cachePrefix + "slideshow";
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				SpecialTube spTube = SpecialTubeModel.getByType("slide", true);
				if (spTube != null) {
					for (String strId : spTube.getListTubeId()) {
						listResult.add(getById(strId));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareHomePageTubeByPlaylist(String playlistId) {
		try {
			listResult = new ArrayList<Tube>();
			limit = SiteConfigModel.get().getHomePageTubePerBlockQuantity();
			String prefix = cacheSide + cachePrefix + "homepagePlaylist_"
					+ playlistId + limit;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("playlistId:" + playlistId);
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareHomePageTubeByChannel(String channelId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "homepageChannel_"
					+ channelId + limit;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("channelId:" + channelId);
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareRelateTube(Tube obj) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "relate_with_id_"
					+ obj.getId();
			mapCacheKey.put(prefix, prefix);
			String playlistKeyPoolPrefix = cachePrefix + "playlistIdPool_"
					+ obj.getPlaylistId();
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				// get pool id by playlist
				List<String> keyPool = new ArrayList<String>();
				if (cache != null && cache.containsKey(playlistKeyPoolPrefix)) {
					keyPool = (ArrayList<String>) cache
							.get(playlistKeyPoolPrefix);
				} else {
					StringBuilder queryString = new StringBuilder();
					queryString.append("playlistId:" + obj.getPlaylistId());

					options = QueryOptions.newBuilder().build();
					System.out.println(queryString.toString());
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);

					if (docResult.getNumberFound() > 0) {
						Tube tub = new Tube();
						for (ScoredDocument scoredDocument : docResult) {
							tub = TubeSearchEngine
									.documentToObjectByReflection(scoredDocument);
							keyPool.add(tub.getId());
						}
					}
					cache.put(playlistKeyPoolPrefix, keyPool);
				}
				// get list id random from pool
				List<String> relateKey = new ArrayList<String>();
				if (keyPool.size() > 20) {
					System.out.println(">20");
					Random randomGenerator = new Random();
					int index = randomGenerator
							.nextInt((int) (keyPool.size() / 2));
					relateKey = keyPool.subList(index, (index + 10));
				} else if (keyPool.size() < 5) {
					System.out.println("<5");
					options = QueryOptions.newBuilder().setLimit(10).build();
					query = Query.newBuilder().setOptions(options).build("");
					docResult = INDEX.search(query);

					if (docResult.getNumberFound() > 0) {
						Tube tub = new Tube();
						for (ScoredDocument scoredDocument : docResult) {
							tub = TubeSearchEngine
									.documentToObjectByReflection(scoredDocument);
							relateKey.add(tub.getId());
						}
					}
				} else {
					System.out.println(">5<20");
					relateKey = keyPool;
				}

				for (String string : relateKey) {
					Tube tub = getById(string);
					if (tub != null) {
						listResult.add(tub);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareMostView() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "mostview_" + limit;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("view")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setLimit(limit)
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options).build("");
				docResult = INDEX.search(query);
				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						listResult.add(TubeSearchEngine
								.documentToObjectByReflection(scoredDocument));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareSearch(String keyword) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "search_" + keyword
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "search_" + keyword
					+ "TotalResult";
			String limitPrefix = cachePrefix + "search_" + keyword + "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
				System.out.println("cache");
			} else {
				StringBuilder queryString = new StringBuilder();
				queryString.append("title:" + keyword + " OR description:"
						+ keyword);
				listResult = new ArrayList<Tube>();
				QueryOptions options;
				Query query;
				Results<ScoredDocument> docResult;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
					System.out.println("count cache");
				} else {
					options = QueryOptions.newBuilder()
							.setNumberFoundAccuracy(1000).build();
					query = Query.newBuilder().setOptions(options)
							.build(queryString.toString());
					docResult = INDEX.search(query);
					totalResult = (int) docResult.getNumberFound();
					System.out.println("count !cache");
				}

				SortOptions sortOptions = SortOptions
						.newBuilder()
						.addSortExpression(
								SortExpression
										.newBuilder()
										.setExpression("bumpPoint")
										.setDirection(
												SortExpression.SortDirection.DESCENDING)
										.setDefaultValueNumeric(1)).build();

				options = QueryOptions
						.newBuilder()
						.setSortOptions(sortOptions)
						.setNumberFoundAccuracy(1000)
						.setLimit(limit)
						.setOffset(limit * (page - 1))
						.setFieldsToReturn("id", "alias", "title",
								"playlistId", "imageUrl", "totalTime", "view")
						.build();

				query = Query.newBuilder().setOptions(options)
						.build(queryString.toString());
				docResult = INDEX.search(query);
				totalReturn = (int) docResult.getNumberReturned();
				if (docResult.getNumberFound() > 0) {
					Tube obj = new Tube();
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
						listResult.add(obj);
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	public static void clearModelCache() {
		initCache();
		try {
			if (mapCacheKey != null && mapCacheKey.size() > 0) {
				for (String key : mapCacheKey.keySet()) {
					cache.remove(key);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
